#ifndef A8_SYNC_H
#define A8_SYNC_H

#include <pthread.h>
#include <a8/sysutils.hpp>
#ifndef _WIN32
#include <sys/time.h>
#endif

namespace a8
{
    namespace sync
    {
        class Mutex
        {
        public:
            Mutex()
            {
                pthread_mutex_init(&mMutex, NULL);
            }
            ~Mutex()
            {
                pthread_mutex_destroy(&mMutex);
            }

            inline int TryLock()
            {
                return pthread_mutex_trylock(&mMutex);
            }

            inline void Lock()
            {
                pthread_mutex_lock(&mMutex);
            }

            inline void UnLock()
            {
                pthread_mutex_unlock(&mMutex);
            }
        private:
            pthread_mutex_t mMutex;
        };

        class Cond
        {
        public:
            Cond()
            {
                pthread_mutex_init(&mMutex, NULL);
                pthread_cond_init(&mCond, NULL);
            }

            ~Cond()
            {
                pthread_cond_destroy(&mCond);
                pthread_mutex_destroy(&mMutex);
            }

			void Signal()
			{
				pthread_mutex_lock(&mMutex);
				pthread_cond_signal(&mCond);
				pthread_mutex_unlock(&mMutex);
			}

            void Wait(unsigned int millisecond)
            {
                const unsigned long long BILLION = 10 * 10000 * 10000;

                struct timeval now;
                gettimeofday(&now, NULL);
                unsigned long long nanosecond = (unsigned long long)now.tv_usec * 1000 + millisecond * (BILLION / 1000);

                struct timespec timeout;
                if (nanosecond < BILLION){
                    timeout.tv_sec = now.tv_sec;
                    timeout.tv_nsec = nanosecond;
                }else{
                    timeout.tv_sec = now.tv_sec + nanosecond / BILLION;
                    timeout.tv_nsec = nanosecond % BILLION;
                }
				
                pthread_mutex_lock(&mMutex);				
                pthread_cond_timedwait(&mCond, &mMutex, &timeout);
                pthread_mutex_unlock(&mMutex);
            }

        private:
            pthread_cond_t mCond;
            pthread_mutex_t mMutex;  
        };
    }
}

#endif
